package jia.ai;

import jia.core.Cartographer;
import jia.game.GenericFlags;
import org.apache.log4j.Logger;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 *
 */
public final class AStarData implements Cartographer {
    //MIN AND MAX are indicative of the size of the overall grid
    private static final int MIN_X=-30;
    private static final int MIN_Y=-30;
    private static final int MAX_X=30;
    private static final int MAX_Y=30;
    private final Set<Point> blockedSet;
    private final Point goalPoint;
    private final Point jumpPoint;

    private static final Logger log=Logger.getLogger(AStarData.class);

    /**
     * @param goalPoint - the XY point to get to.
     * @param blockedPoints - array of point objects that represent blocked areas
     * @param jumpPoint - origin point?
     */
    public AStarData(Point goalPoint, Point[] blockedPoints, Point jumpPoint) {
        this.goalPoint=goalPoint;
        blockedSet=new HashSet<Point>();
        for (Point blockedPoint : blockedPoints) {
            blockedSet.add(blockedPoint);
        }
        this.jumpPoint=jumpPoint;
    }

    /**
     * @param goalPoint - the XY point to get to.
     * @param blockedPoints - array of point objects that represent blocked areas
     */
    public AStarData(Point goalPoint, Point[] blockedPoints) {
        this(goalPoint, blockedPoints, null);
    }

    /**
     * @param node
     *
     * @return
     */
    public double estimateCostToGoal(Object node) {
        return getCostToAdjacentNode(node, goalPoint);
    }

    /**
     * @param node
     *
     * @return
     */
    public Iterator getAdjacentNodes(Object node) {
        Point nodePoint=(Point) node;
        int x=nodePoint.x;
        int y=nodePoint.y;
        List<Point> list=new ArrayList<Point>();
        try {
            if (nodePoint.equals(jumpPoint)) {
                list.add(goalPoint);
                return list.iterator();
            }
            for (int offsetX=-1; offsetX < 2; offsetX++) {
                for (int offsetY=-1; offsetY < 2; offsetY++) {
                    if ((offsetX == 0) && (offsetY == 0)) {
                        continue;
                    }
                    int newX=x + offsetX;
                    int newY=y + offsetY;
                    if ((newX < MIN_X) || (newY < MIN_Y) || (newX > MAX_X) || (newY > MAX_Y)) {
                        continue;
                    }
                    Point point=new Point(newX, newY);
                    if (!blockedSet.contains(point)) {
                        list.add(point);
                    }
                }
            }
        } catch (Exception e) {
            if (GenericFlags.errorMessages) {
                log.error("An error occured while getting the adjacent nodes " + e.getMessage());
            }
        }
        return list.iterator();
    }

    /**
     * @param node
     *
     * @return
     */
    public ArrayList getAdjacentNodesAsList(Object node) {
        Point nodePoint=(Point) node;
        int x=nodePoint.x;
        int y=nodePoint.y;
        ArrayList<Point> list=new ArrayList<Point>();
        try {
            if (nodePoint.equals(jumpPoint)) {
                list.add(goalPoint);
                return list;
            }
            for (int offsetX=-1; offsetX < 2; offsetX++) {
                for (int offsetY=-1; offsetY < 2; offsetY++) {
                    if ((offsetX == 0) && (offsetY == 0)) {
                        continue;
                    }
                    int newX=x + offsetX;
                    int newY=y + offsetY;
                    if ((newX < MIN_X) || (newY < MIN_Y) || (newX > MAX_X) || (newY > MAX_Y)) {
                        continue;
                    }
                    Point point=new Point(newX, newY);
                    if (!blockedSet.contains(point)) {
                        list.add(point);
                    }
                }
            }
        } catch (Exception e) {
            if (GenericFlags.errorMessages) {
                log.error("An error occured while getting the adjacent nodes " + e.getMessage());
            }
        }
        return list;
    }

    /**
     * @param fromNode
     * @param toNode
     *
     * @return
     */
    public double getCostToAdjacentNode(Object fromNode, Object toNode) {
        if (fromNode.equals(jumpPoint)) {
            return 0.0;
        }
        return ((Point) fromNode).distance((Point) toNode);
    }

    /**
     * @param node
     *
     * @return
     */
    public boolean isGoalNode(Object node) {
        return node.equals(goalPoint);
    }
}